using System;

namespace KodeIT.Web
{
    /// <summary>
    /// This class represents the event raised by IIS at the end of a request, just before IIS writes the 
    /// transaction to the IIS log. If your HttpFilter should be notified for this event, it should 
    /// subscribe for the <see cref="IFilterEvents.AccessDenied"/> event.
    /// </summary>
    /// <remarks>
    /// When the server is about to log information to the server log file, this event is raised by IIS. 
    /// The properties can be replaced by new values so that IIS uses these new values when writing the 
    /// information to the IIS log.
    /// </remarks>
    public class LogEvent : ContextEvent
    {
        private bool _overwritten;

        private LogBuffers _logBuffers;
        private NativeData<int> _httpStatus;
        private NativeData<int> _win32Status;
        private NativeData<int> _bytesSent;
        private NativeData<int> _bytesRecvd;
        private NativeData<int> _msTimeForProcessing;

        internal LogEvent(
            NativeContext nativeContext,
            int clientHostNameLength,
            int clientUserNameLength,
            int serverNameLength,
            int operationLength,
            int targetLength,
            int parametersLength,
            int httpStatus,
            int win32Status,
            int bytesSent,
            int bytesRecvd,
            int msTimeForProcessing)
            : base(nativeContext)
        {
            NativeGetter getter = new NativeGetter(Populate);

            _logBuffers = new LogBuffers();
            _logBuffers._clientHostName = new NativeString(clientHostNameLength, getter);
            _logBuffers._clientUserName = new NativeString(clientUserNameLength, getter);
            _logBuffers._serverName = new NativeString(serverNameLength, getter);
            _logBuffers._operation = new NativeString(operationLength, getter);
            _logBuffers._target = new NativeString(targetLength, getter);
            _logBuffers._parameters = new NativeString(parametersLength, getter);

            _httpStatus = new NativeData<int>(httpStatus);
            _win32Status = new NativeData<int>(win32Status);
            _bytesSent = new NativeData<int>(bytesSent);
            _bytesRecvd = new NativeData<int>(bytesRecvd);
            _msTimeForProcessing = new NativeData<int>(msTimeForProcessing);
        }

        /// <summary>
        /// Gets or sets the client's host name.
        /// </summary>
        public string ClientHostName
        {
            get { return _logBuffers._clientHostName.Data; }
            set
            {
                if (String.IsNullOrEmpty(value))
                {
                    throw new ArgumentNullException("value",
                        "The client host name cannot be null or empty");
                }

                _overwritten = true;
                _logBuffers._clientHostName.Data = value;
            }
        }

        /// <summary>
        /// Gets or sets the client's user name.
        /// </summary>
        public string ClientUserName
        {
            get { return _logBuffers._clientUserName.Data; }
            set
            {
                if (String.IsNullOrEmpty(value))
                {
                    throw new ArgumentNullException("value",
                        "The client user name cannot be null or empty");
                }

                _overwritten = true;
                _logBuffers._clientUserName.Data = value;
            }
        }

        /// <summary>
        /// Gets or sets the name of the server to which the client is connected.
        /// </summary>
        public string ServerName
        {
            get { return _logBuffers._serverName.Data; }
            set
            {
                if (String.IsNullOrEmpty(value))
                {
                    throw new ArgumentNullException("value",
                        "The server name cannot be null or empty");
                }

                _overwritten = true;
                _logBuffers._serverName.Data = value;
            }
        }

        /// <summary>
        /// Gets or sets the HTTP operation type.
        /// </summary>
        public string Operation
        {
            get { return _logBuffers._operation.Data; }
            set
            {
                if (String.IsNullOrEmpty(value))
                {
                    throw new ArgumentNullException("value",
                        "The operation cannot be null or empty");
                }

                _overwritten = true;
                _logBuffers._operation.Data = value;
            }
        }

        /// <summary>
        /// Gets or sets the target of the HTTP command.
        /// </summary>
        public string Target
        {
            get { return _logBuffers._target.Data; }
            set
            {
                if (String.IsNullOrEmpty(value))
                {
                    throw new ArgumentNullException("value",
                        "The target cannot be null or empty");
                }

                _overwritten = true;
                _logBuffers._target.Data = value;
            }
        }

        /// <summary>
        /// Gets or sets the parameters passed to the HTTP command.
        /// </summary>
        public string Parameters
        {
            get { return _logBuffers._parameters.Data; }
            set
            {
                if (String.IsNullOrEmpty(value))
                {
                    throw new ArgumentNullException("value",
                        "The parameters cannot be null or empty");
                }

                _overwritten = true;
                _logBuffers._parameters.Data = value;
            }
        }

        /// <summary>
        /// Gets or sets the HTTP return status.
        /// </summary>
        public int LogHttpStatus
        {
            get { return _httpStatus.Data; }
            set
            {
                _overwritten = true;
                _httpStatus.Data = value;
            }
        }

        /// <summary>
        /// Gets or sets the Win32 error code.
        /// </summary>
        public int Win32Status
        {
            get { return _win32Status.Data; }
            set
            {
                _overwritten = true;
                _win32Status.Data = value;
            }
        }

        /// <summary>
        /// Gets or sets the number of bytes sent from the server to the client.
        /// </summary>
        public int BytesSent
        {
            get { return _bytesSent.Data; }
            set
            {
                _overwritten = true;
                _bytesSent.Data = value;
            }
        }

        /// <summary>
        /// Gets or sets the number of bytes received by the server from the client.
        /// </summary>
        public int BytesRecvd
        {
            get { return _bytesRecvd.Data; }
            set
            {
                _overwritten = true;
                _bytesRecvd.Data = value;
            }
        }

        /// <summary>
        /// Gets or sets the time in milliseconds required to process the client request.
        /// </summary>
        public int MsTimeForProcessing
        {
            get { return _msTimeForProcessing.Data; }
            set
            {
                _overwritten = true;
                _msTimeForProcessing.Data = value;
            }
        }

        private void Populate()
        {
            NativeMethods.GetLog(NativeContext, _logBuffers);
        }

        internal override FilterStatus Update()
        {
            if (_overwritten)
            {
                NativeMethods.SetLog(
                    NativeContext, 
                    _logBuffers,
                    _httpStatus.Data,
                    _win32Status.Data,
                    _bytesSent.Data,
                    _bytesRecvd.Data,
                    _msTimeForProcessing.Data);
            }
            return base.Update();
        }
    }
}
